package fun.xinnix.concurrent;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;

import java.util.*;
import java.util.concurrent.*;

/**
 * @author Administrator
 */
public class CountDownLatchTest {
    private static final int COUNT = 3;
    private static final Log log = LogFactory.get();
    private static final List<User> USER_LIST = User.getUserList();
    private static final int USER_SIZE = 100_000_000;

    public static void main(String[] args) {
        TimeInterval timer = DateUtil.timer();
        long timeStart = timer.intervalRestart();
        log.info("time-start:{}", timeStart);

        ordinaryForeach();
        long time = timer.intervalRestart();
        log.info("time-re:{}", time);

        completableFutureTest3();
        long time1 = timer.intervalRestart();
        log.info("time1:{}", time1);

        countDownLatchTest2();
        long time2 = timer.intervalRestart();
        log.info("time2:{}", time2);

        ordinaryForeach();
        long time3 = timer.intervalRestart();
        log.info("time3:{}", time3);

        cyclicBarrierTest();
        long time4 = timer.intervalRestart();
        log.info("time4:{}", time4);
    }

    public static void cyclicBarrierTest() {
        ThreadPoolExecutor threadPool = ThreadPoolDemo.getThreadPool();
        Map<String, Object> result = new HashMap<>(16);
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> log.info("cyclicBarrier"));
        Future<Integer> sumFuture = threadPool.submit(() -> {
            Integer ageSum = getAgeSum();
            result.put("sum", ageSum);
            // 等待其它线程
            cyclicBarrier.await();
            return ageSum;
        });
        threadPool.execute(() -> {
            result.put("max", getAgeMax());
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        });
        threadPool.execute(() -> {
            result.put("min", getAgeMin());
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        });
        try {
            result.put("avg", sumFuture.get() / USER_LIST.size());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        log.info("cyclicBarrierTest finish:{}", result);
        threadPool.shutdown();
    }

    public static void ordinaryForeach() {
        Map<String, Object> result = new HashMap<>(16);
        int sum = getAgeSum();
        int max = getAgeMax();
        int min = getAgeMin();
        result.put("avg", sum / USER_LIST.size());
        result.put("sum", sum);
        result.put("max", max);
        result.put("min", min);
        log.info("ordinaryForeach finish:{}", result);
    }

    public static void countDownLatchTest2() {
        ThreadPoolExecutor threadPool = ThreadPoolDemo.getThreadPool();
        Map<String, Object> result = new HashMap<>(16);
        // 指定多个线程执行,一个线程执行完成之后,必须等待其它线程执行完才算结束
        CountDownLatch countDownLatch = new CountDownLatch(COUNT);
        Future<Integer> sum = threadPool.submit(() -> {
            try {
                return getAgeSum();
            } finally {
                countDownLatch.countDown();
            }
        });
        Future<Integer> max = threadPool.submit(() -> {
            try {
                return getAgeMax();
            } finally {
                countDownLatch.countDown();
            }
        });
        Future<Integer> min = threadPool.submit(() -> {
            try {
                return getAgeMin();
            } finally {
                countDownLatch.countDown();
            }
        });
        try {
            result.put("avg", sum.get() / USER_LIST.size());
            result.put("sum", sum.get());
            result.put("max", max.get());
            result.put("min", min.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadPool.shutdown();
        log.info("countDownLatchTest finish:{}", result);
    }

    public static void completableFutureTest3() {
        Map<String, Object> result = new HashMap<>(16);

        CompletableFuture<Integer> sum = CompletableFuture.supplyAsync(CountDownLatchTest::getAgeSum)
                .whenComplete((res, e) -> {
                    result.put("sum", res);
                    result.put("avg", res / USER_LIST.size());
                    log.info("CompletableFuture-sum");
                });

        CompletableFuture<Integer> max = CompletableFuture.supplyAsync(CountDownLatchTest::getAgeMax)
                .whenComplete((res, e) -> {
                    result.put("max", res);
                    log.info("CompletableFuture-max");
                });

        CompletableFuture<Integer> min = CompletableFuture.supplyAsync(CountDownLatchTest::getAgeMin)
                .whenComplete((res, e) -> {
                    result.put("min", res);
                    log.info("CompletableFuture-min");
                });

        CompletableFuture<Void> handleFuture = CompletableFuture.allOf(sum, max, min);
        log.info("CompletableFuture-join");
        handleFuture.join();
        log.info("completableFutureTest3 finish:{}", result);
    }

    private static int getAgeSum() {
        return USER_LIST.stream().mapToInt(User::getAge).sum();
    }

    private static Integer getAgeMin() {
        return USER_LIST.stream().mapToInt(User::getAge).min().orElse(0);
    }

    private static Integer getAgeMax() {
        return USER_LIST.stream().mapToInt(User::getAge).max().orElse(0);
    }


    public static void completableFutureTest1() {
        CompletableFuture<Void> thread1 = CompletableFuture.runAsync(() -> log.info("thread1 do some thing"));
        CompletableFuture<Void> thread2 = CompletableFuture.runAsync(() -> log.info("thread2 do some thing"));
        CompletableFuture<Void> thread3 = CompletableFuture.runAsync(() -> log.info("thread3 do some thing"));
        CompletableFuture<Void> thread4 = CompletableFuture.runAsync(() -> log.info("thread4 do some thing"));
        CompletableFuture<Void> handleFuture = CompletableFuture.allOf(thread1, thread2, thread3, thread4);
        handleFuture.join();
        log.info("thread finish");
    }

    static class User {
        private Integer age;

        public void setAge(Integer age) {
            this.age = age;
        }

        public Integer getAge() {
            return this.age;
        }

        public User() {
        }

        public User(Integer age) {
            this.age = age;
        }

        public static List<User> getUserList() {
            List<User> list = new ArrayList<>();
            for (int i = 0; i < USER_SIZE; i++) {
                list.add(new User(RandomUtil.randomInt(10, 30)));
            }
            log.info("initiation USER_LIST complete");
            return list;
        }
    }
}
